home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / CBASE102.ARJ / GUIDE.TXT < prev    next >
Text File  |  1991-09-23  |  86KB  |  2,252 lines

  1.                                  cbaseTM
  2.  
  3.                         The C Database Library
  4.  
  5.  
  6.  
  7.  
  8.  
  9.                                 Citadel
  10.                           Brookville, Indiana Copyright (c) 1989, 1991 Citadel
  11. All rights reserved
  12.  
  13. Citadel Software, Inc.
  14. 241 East Eleventh Street
  15. Brookville, IN 47012
  16. 317-647-4720
  17. BBS 317-647-2403
  18.  
  19. Version 1.0.2
  20.  
  21. This manual is protected by United States copyright law.  No part of it
  22. may be reproduced without the express written permission of Citadel.
  23.  
  24. Technical Support
  25. The Citadel BBS is available 24 hours a day.  Voice support is available
  26. between 10 a.m. and 4 p.m. EST.  When calling for technical support,
  27. please have ready the following information:
  28.  
  29.     - product name and version number
  30.     - operating system and version number
  31.     - C compiler and version number
  32.     - computer brand and model
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64. UNIX is a trademark of AT&T.  Turbo C is a trademark of Borland
  65. International, Inc.
  66.  
  67.  
  68.  
  69.  
  70.                                                                Contents
  71.  
  72.  
  73. Introduction                                                          1
  74.  
  75. Chapter 1.  A Tutorial Introduction                                   3
  76.     1.1    Defining a Database
  77.     1.2    Using the cbase Library
  78.  
  79. Chapter 2.  cbase Architecture                                        9
  80.  
  81. Chapter 3.  The Data Definition Language                             11
  82.  
  83. Chapter 4.  cbase Library Functions                                  13
  84.     4.1    Access Control Functions
  85.     4.2    Lock Functions
  86.     4.3    Record Cursor Position Functions
  87.     4.4    Key Cursor Position Functions
  88.     4.5    Input/Output Functions
  89.     4.6    Import/Export Functions
  90.  
  91. Chapter 5.  Custom Indexing                                          23
  92.  
  93. Chapter 6.  An Example Program                                       27
  94.     6.1    Data Definition
  95.     6.2    Opening a cbase
  96.     6.3    Locking a cbase
  97.     6.4    Accessing a cbase
  98.     6.5    Closing a cbase
  99.     6.6    Storing Variable Length Text
  100.  
  101. Appendix A.  Installation Instructions                               37
  102.     A1    manx
  103.     A2    The blkio Library
  104.     A3    The lseq Library
  105.     A4    The btree Library
  106.     A5    The cbase Library
  107.     A6    Combining Libraries
  108.     A7    cbddlp
  109.     A8    rolodeck
  110.     A9    Troubleshooting
  111.  
  112. Appendix B.  Defining New Data Types                                 45
  113.     B1    The Type Name
  114.     B2    The Comparison Function
  115.     B3    The Export and Import Functions
  116.     B4    The Type Count
  117.  
  118. Appendix C.  Porting to a New Operating System                      49
  119.     C1    The OPSYS and CCOM Macros
  120.     C2    The File Descriptor Type
  121.     C3    System Calls for File Access
  122.     C4    System Calls for File Locking 
  123.     C5    Debugging
  124.  
  125. References                                                           53
  126.  
  127.  
  128.  
  129.  
  130.                                                            Introduction
  131.  
  132.  
  133.      cbase is a complete multiuser C database file management library,
  134. providing indexed and sequential access on multiple keys.  Custom
  135. indexing beyond that performed automatically by cbase can also be
  136. performed.  cbase features a layered architecture (see Figure 2.1), and
  137. actually includes four individual libraries.  Below is a summary of the
  138. library's main features.
  139.  
  140.  
  141.                             cbase Features
  142.  
  143. Portable
  144.   - Written in strict adherence to ANSI C standard.
  145.   - K&R C compatibility maintained.
  146.   - All operating system dependent code is isolated, making it easy to
  147.     port to new systems easy.
  148.   - UNIX and DOS currently supported.
  149.   - Complete C source code included.
  150. Buffered
  151.   - Both records and indexes are buffered using LRU (least recently
  152.     used) buffering.
  153. Fast and efficient random access
  154.   - B+-trees are used for inverted file key storage.
  155.   - Multiple keys are supported.
  156.   - Both unique and duplicate keys are supported.
  157. Fast and efficient sequential access
  158.   - B+-trees also allow keyed sequential access.
  159.   - Records are stored in doubly linked lists for non-keyed sequential
  160.     access.
  161.   - Both types of sequential access are bidirectional.
  162. Multiuser
  163.   - Read-only locking.
  164. Other Features
  165.   - Text file data import and export.
  166.   - Custom data types can be defined.
  167.   - Marker used to detect corrupt files.
  168.   - Reference documentation is in standard UNIX manual entry format,
  169.     including errno values.
  170. Utilities
  171.   - cbddlp, a data definition language processor, is provided to
  172.     automatically generate the C code defining a database.
  173.  
  174.  
  175.  
  176.  
  177.                                     Chapter 1:  A Tutorial Introduction
  178.  
  179.  
  180.      We begin with a brief example of a cbase application to provide the
  181. reader with a general understanding of the basic elements involved. 
  182. Details on everything presented here will come in later chapters.  The
  183. running example in this tutorial will be a minimal inventory database
  184. consisting of a single type of record having fields for a unique part
  185. code, a part description, bin location, and quantity in stock.
  186.  
  187.  
  188. 1.1  Defining a Database
  189.  
  190.      The first step in any database application is to design the logical
  191. structure of the database, i.e., the records to be stored and the fields
  192. in those records.  This logical design must then be encoded somehow into
  193. the application, which involves the construction of data structures that
  194. can be quite lengthy and tedious to input.  To facilitate this process,
  195. cbase allows databases to be defined using a relatively concise data
  196. definition language (DDL).
  197.  
  198.      The most important DDL element is the record statement.  record is
  199. similar to the C struct statement, but database data types are used
  200. rather than C types, and fields can be made keys simply by prefixing the
  201. keyword key.  Using the keyword unique in addition will cause the key to
  202. be constrained to be unique.  What being a key means is that an index is
  203. automatically maintained for that field, allowing quick searches to be
  204. performed on that field as well as rapid sequential processing of the
  205. records in the sort order of that field.  The DDL statements data file
  206. and index file are used to specify the filename for each data file and
  207. index file.  C preprocessor statements can also be included in a DDL
  208. file.
  209.  
  210.      Figure 1.1 shows the complete DDL description part.ddl for our
  211. inventory database.  This information must now be translated into a form
  212. accessible from a C program.  This is done with the cbase DDL processor
  213. cbddlp.
  214.  
  215.     cbddlp part.ddl
  216.  
  217. From the information in part.ddl, cbddlp generates all the necessary
  218. macros and data structures necessary to completely define the database
  219. in C.  Two C source files are generated:  a .h file to be included in
  220. every module, and a .i file to be included in only one, normally the one
  221. containing main.  The contents of the .i file will be used only
  222. internally by cbase, but the contents of the .h file are required by the
  223. application program.
  224.  
  225.      Figure 1.2 lists the header file part.h generated from part.ddl. 
  226. First notice that the C preprocessor statements have been passed through
  227. unaltered.  There is then a macro identifying the cbase that is the
  228. record name converted to upper case.  For each record statement in the
  229. DDL file, there is a corresponding C struct statement using the same
  230. record name as its identifier.  For each field in a record there is an
  231. upper case macro identifying it, and a macro for the number of fields in
  232. the record.  Finally, there is a declaration for the field list data
  233. structure in the .i file that is passed to the cbase library functions
  234. to create and open a cbase.  The prefix for the field count and field
  235. list identifiers are taken from the characters of the first field name
  236. preceding the first underscore.
  237.  
  238.  
  239. /* constants */
  240. #define PTCODE_MAX  (11)    /* part code length max */
  241. #define PTDESC_MAX  (30)    /* part description length max */
  242. #define PTBIN_MAX   (4)     /* part bin length max */
  243.  
  244. /* file assignments */
  245. data  file "part.dat"   contains part;
  246. index file "ptcode.ndx" contains pt_code;
  247. index file "ptdesc.ndx" contains pt_desc;
  248.  
  249. /* record definitions */
  250. record part {                                   /* part record */
  251.     unique key t_string pt_code[PTCODE_MAX];    /* code */
  252.     key t_string        pt_desc[PTDESC_MAX];    /* description */
  253.     t_string            pt_bin[PTBIN_MAX];      /* storage location */
  254.     t_long              pt_stock;               /* quantity in stock */
  255. };
  256.  
  257.               Figure 1.1. Definition of the Part Database
  258.  
  259.  
  260. #ifndef H_PART
  261. #define H_PART
  262.  
  263. /* libray headers */
  264. #include <cbase.h>
  265.  
  266. #define PTCODE_MAX  (11)    /* part code length max */
  267. #define PTDESC_MAX  (30)    /* part description length max */
  268. #define PTBIN_MAX   (4)     /* part bin length max */
  269.  
  270. /* record name */
  271. #define PART    "part.dat"
  272.  
  273. /* part record definition */
  274. typedef struct part {
  275.     char pt_code[PTCODE_MAX];
  276.     char pt_desc[PTDESC_MAX];
  277.     char pt_bin[PTBIN_MAX];
  278.     long pt_stock;
  279. } part_t;
  280.  
  281. /* field names for record part */
  282. #define PT_CODE     (0)
  283. #define PT_DESC     (1)
  284. #define PT_BIN      (2)
  285. #define PT_STOCK    (3)
  286. #define PTFLDC      (4)
  287.  
  288. /* field definition list for record part */
  289. extern cbfield_t ptfldv[PTFLDC];
  290.  
  291. #endif
  292.  
  293.                  Figure 1.2. Part Database Header File
  294.  
  295.  
  296.  
  297. 1.2  Using the cbase Library
  298.  
  299.      Figure 1.3 lists a skeletal part database application.  Points to
  300. notice are the inclusion of the database definition headers, registering
  301. the bcloseall function, and the use of the macros and data structures
  302. generated by cbddlp to create and open the database.
  303.  
  304. /* ansi headers */
  305. #include <errno.h>
  306. #include <stdio.h>
  307. #include <stdlib.h>
  308.  
  309. /* library headers */
  310. #include <cbase.h>
  311.  
  312. /* local headers */
  313. #include "part.h"
  314. #include "part.i"
  315.  
  316. int main(int argc, char *argv[])
  317. {
  318.     cbase_t *   cbp     = NULL;
  319.     int         found   = 0;
  320.     struct part pt;
  321.  
  322.     /* register termination function to flush database buffers */
  323.     if (atexit(bcloseall)) {
  324.         perror("atexit");
  325.         exit(EXIT_FAILURE);
  326.     }
  327.  
  328.     /* create cbase */
  329.     if (cbcreate(PART, sizeof(struct part), PTFLDC, ptfldv) == -1) {
  330.         if (errno != EEXIST) {
  331.             fprintf(stderr, "cbcreate:  error %d.\n", errno);
  332.             exit(EXIT_FAILURE);
  333.         }
  334.     }
  335.  
  336.     /* open cbase */
  337.     cbp = cbopen(PART, "r+", PTFLDC, ptfldv);
  338.     if (cbp == NULL) {
  339.         fprintf(stderr, "cbopen:  error %d.\n", errno);
  340.         exit(EXIT_FAILURE);
  341.     }
  342.  
  343.     /*
  344.      *
  345.      */
  346.  
  347.     /* close cbase */
  348.     if (cbclose(cbp) == -1) {
  349.         fprintf(stderr, "cbclose:  error %d.\n", errno);
  350.         exit(EXIT_FAILURE);
  351.     }
  352.  
  353.     exit(EXIT_SUCCESS);
  354. }
  355.  
  356.             Figure 1.3.  Skeletal Part Database Application
  357.  
  358. This program would be completed by adding the main body of code to
  359. interact with the user and perform the database operations necessary to
  360. satisfy his requests.  Below is a quick overview of some of the basic
  361. database functions and how they are used.
  362.  
  363.      Most database operations are relative to the record cursor.  The
  364. record cursor is positioned either on a record or the special position
  365. null.  Strict attention must be paid to the effect every function used
  366. has on the record cursor.
  367.  
  368.      A record is stored in a database with the cbinsert function.  The
  369. following stores the record pt in the cbase cbp.  Since the part cbase
  370. has a unique key, the part code, cbinsert will fail and set errno to
  371. CBEDUP if there is already a record in the cbase with that part code.
  372.  
  373.     if (cbinsert(cbp, &pt) == -1) {
  374.         if (errno == CBEDUP) {
  375.             fprintf(stderr, "Part code %.*s already used.\n",
  376.                                        sizeof(pt.pt_code), pt.pt_code);
  377.         } else {
  378.             fprintf(stderr, "cbinsert:  error %d.\n", errno);
  379.         }
  380.     }
  381.  
  382. The record cursor is placed on the newly inserted record.  Note that the
  383. error message takes into account that this database does not store the
  384. terminating nul character of the part code string.
  385.  
  386.      Records can be directly located based on any of its keys using the
  387. cbkeysrch function.  cbkeysrch returns a value of zero if there is no
  388. record with that key value, and positions the record cursor on the record
  389. with the next higher key.  If a match is found, the record cursor is
  390. positioned on the match and a value of one returned.
  391.  
  392.     found = cbkeysrch(cbp, PT_CODE, pt.pt_code);
  393.     if (found == 0) {
  394.         fprintf(stderr, "Key not found.\n");
  395.     }
  396.  
  397.      A record is deleted by first positioning the record cursor to that
  398. record, then calling cbdelcur to delete the current record.
  399.  
  400.      cbdelcur(cbp);
  401.  
  402.      The need often arises to process a sorted sequence of records.  This
  403. is done by first positioning the cursor to the first record to be
  404. processed, then using cbkeynext in a loop to step through each record in
  405. the order defined by the specified key.  The macro cbkcursor can be used
  406. to test when the last record has been processed.  The following code
  407. fragment prints all part codes above a given value.
  408.  
  409.     cbkeysrch(cbp, PT_CODE, pt.pt_code);
  410.     while (cbkcursor(cbp, PT_CODE) != NULL) {
  411.         cbgetr(cbp, &pt);
  412.         printf("%.*s", sizeof(pt.pt_code), pt.pt_code);
  413.         cbkeynext(cbp, PT_CODE);
  414.     }
  415.  
  416.      There has been some simplification on cursors in this tutorial. 
  417. There is actually one cursor for the record and a separate cursor for
  418. each key.  The relationship between these cursors will be covered in
  419. Chapter 4.
  420.  
  421.  
  422.  
  423.  
  424.                                          Chapter 2:  cbase Architecture
  425.  
  426.  
  427.      cbase is designed around a four-layered architecture, the layers
  428. being:  File System, Buffered I/O, File Structure, and ISAM (Figure 1.1). 
  429. The nethermost layer is the File System, which is part of the operating
  430. system.  This layer is accessed via system calls, an interface which
  431. varies from system to system.  On top of the File System layer, the
  432. Buffered I/O layer performs two primary functions:  to provide a portable
  433. interface to the file system, and to perform buffering.  The stdio
  434. library also performs these same two functions, but it models a file as
  435. an unstructured stream of characters and is intended primarily for text
  436. files.  The blkio library, on the other hand, is designed for database
  437. file access and models a file as a collection of blocks made up of fields
  438. (see FROS89 for a complete description of blkio).
  439.  
  440.  
  441.                   ┌─────────────────────────────────┐
  442.                   │              ISAM               │
  443.                   ├─────────────────────────────────┤
  444.                   │         File Structure          │
  445.                   ├─────────────────────────────────┤
  446.                   │          Buffered I/O           │
  447.                   ├─────────────────────────────────┤
  448.                   │           File System           │
  449.                   └─────────────────────────────────┘
  450.  
  451.                      (a). Database Reference Model
  452.  
  453.                   ┌─────────────────────────────────┐
  454.                   │              cbase              │
  455.                   ├────────────────┬────────────────┤
  456.                   │     lseq       │      btree     │
  457.                   ├────────────────┴────────────────┤
  458.                   │              blkio              │
  459.                   ├─────────────────────────────────┤
  460.                   │          system calls           │
  461.                   └─────────────────────────────────┘
  462.  
  463.              (b) Relation of Libraries to Reference Model
  464.  
  465.                     Figure 2.1. cbase Architecture
  466.  
  467.  
  468.      The File Structure layer is the most complex.  This is where the
  469. actual file organizations are defined.  Since different file structures
  470. are suited to different tasks, there is more than a single library at
  471. this layer; currently implemented are btree, a B+-tree file management
  472. library, and lseq, a doubly-linked sequential file management library. 
  473. At the top of the reference model is the ISAM layer.  ISAM stands for
  474. Indexed Sequential Access Method, and is the interface typically used in
  475. database applications.  As the name says, this layer provides both direct
  476. random access to records via indexes, as well as the sequential
  477. processing of records.  There is only a single library at this library,
  478. called cbase, short for C Database.  cbase internally uses lseq for
  479. record storage and btree to automatically maintain indexes for those
  480. records.  The relation of each index to the data is referred to as an
  481. "inverted file" (see ULLM82 and Chapter 5).
  482.  
  483.  
  484.  
  485.  
  486.  
  487.  
  488.                                Chapter 3:  The Data Definition Language
  489.  
  490.  
  491.      The first step in the development of a database application is to
  492. define the logical structure of the database.  This requires C data
  493. structures that, while not necessarily complex, can be lengthy and
  494. tedious to construct.  cbase therefore provides a utility to
  495. automatically generate the necessary C code from a relatively short and
  496. simple description written in a data definition language (DDL).
  497.  
  498.      The cbase data definition language processor, cbddlp, takes the name
  499. of a DDL source file as its only argument.  This file must have the
  500. extension .ddl.
  501.  
  502.      cbddlp database.ddl
  503.  
  504. From the contents of this input file, two C source files are generated. 
  505. The first is a header file to be included by every source file that will
  506. access the database.  This file has the same base name as the DDL file
  507. with the extension .h.  The second is also an include file, but it is to
  508. be included in only one source file (normally the one containing main)
  509. in each application accessing the database, because it contains actual
  510. data.  This file has the same base name as the DDL file with the
  511. extension .i.
  512.  
  513.      There are three types of statements in a DDL file.  First, any C
  514. preprocessor statements may appear in a DDL file; they are simply passed
  515. through to the generated .h file.  This allows macros for field array
  516. element counts to be defined, or other header files containing such
  517. definitions to be included.  Second, file assignments are made by the
  518. following two statements:
  519.  
  520.      data file  "recname.dat" contains recname;
  521.      index file "ndxname.ndx" contains ndxname;
  522.  
  523. These simply specify that the filename in quotes is to be used for the
  524. following record or index.  The extensions .dat and .ndx are not required
  525. by cbase.  Lastly is the record statement, which is used to actually
  526. define the format of the database.
  527.  
  528.     record recname {
  529.         [[unique] key] dbtype fldname[\[elemc\]];
  530.         ...
  531.     };
  532.  
  533. The record statement is very similarly to the C struct statement.  dbtype
  534. is a cbase data type.  A field is specified to be a key simply by using
  535. the key specifier, and the key will be constrained to be unique by
  536. further adding the unique specifier.  C-style comments may also be used
  537. in DDL files.  A complete DDL file will be included in the example of
  538. Chapter 6.
  539.  
  540.      For the predefined cbase data types, cbddlp knows the corresponding
  541. C data type to use in generating the C structures for the database.  If
  542. a user has defined a new data type, the corresponding C data type must
  543. be specified explicitly.  This is done by following the user-defined
  544. cbase data type by a colon and the corresponding C data type.
  545.  
  546.     [[unique] key] dbtype:ctype fldname[\[elemc\]]
  547.  
  548. cbddlp can also be modified to automatically recognize user-defined
  549. types.  See the readme file accompanying the source code for cbddlp for
  550. instructions.
  551.  
  552.      First, there is a macro for the cbase name.  This macro is the
  553. record name converted to upper case.  Second, a C structure is defined
  554. that exactly corresponds to each record in the DDL file.  The name of
  555. this structure is the same as the record name; it is also typedefed to
  556. the record name with _t appended.  Third, a macro is defined for each
  557. field in the record; these are used to specify the desired field to the
  558. cbase library functions.  The field macros are the field names converted
  559. to upper case, so the field names must be unique across all the records
  560. in use by an application.  Finally, there is a macro for the field count
  561. and a declaration for the field list.  These are made unique from other
  562. cbases by using the first characters (up to four) up to an underscore in
  563. the first field name as a prefix.  For example, for a record having the
  564. first field rd_name, the field count and field list would be RDFLDC and
  565. rdfldv.  These are used in creating and opening a cbase.  The actual
  566. definition of the field list is contained in the generated .i file.
  567.  
  568.      cbddlp can be easily integrated into make with the following suffix
  569. rules.
  570.  
  571.     # suffix rules
  572.     .SUFFIXES:  .ddl .h .i
  573.  
  574.     .ddl.h:
  575.         cbddlp $<
  576.  
  577.     .ddl.i:
  578.         cbddlp $<
  579.  
  580. These are for the standard UNIX make.  The exact statements may vary for
  581. other versions.
  582.  
  583.  
  584.  
  585.  
  586.                                     Chapter 4:  cbase Library Functions
  587.  
  588.  
  589.      The main cbase library functions are presented in this chapter
  590. grouped by function.  For further details, see the alphabetically ordered
  591. reference manual entries.  The cbase functions use the ANSI error
  592. variable errno for error reporting.  To avoid conflict with existing
  593. error numbers (defined in <errno.h>), negative values are used.  Macros
  594. for these values are defined in the header file for cbase and also the
  595. underlying libraries.
  596.  
  597.  
  598. 4.1  Access Control Functions
  599.  
  600.      The cbcreate function is used to create a new cbase.
  601.  
  602.     int cbcreate(const char *cbname, size_t recsize,
  603.                                     int fldc, const cbfield_t fldv[]);
  604.  
  605. cbname points to a character string which is the name of the cbase.  This
  606. name is used as the name of the data file containing the records in the
  607. cbase.  recsize specifies the record size to be used.  fldc is the number
  608. of fields in the cbase, and fldv is an array of fldc field definition
  609. structures.  The field count macro and field definition list created by
  610. cbddlp should be used for fldc and fldv.
  611.  
  612.      The field list will generated by cbddlp will normally just be passed
  613. directly to cbcreate without the application becoming involved in its
  614. contents.  In some instances, however, it is necessary to directly
  615. manipulate the field list fldv.  For instance, it is sometimes desired
  616. to dynamically change the name of the index files, and so the internal
  617. structure of this list will be explained.
  618.  
  619.      Field definitions must be listed in the order the fields occur in
  620. the record; the field macros generated by cbddlp are used to index into
  621. this array, the first field being zero.  The field definition structure
  622. type cbfield_t is defined in <cbase.h>.
  623.  
  624.     typedef struct {        /* field definition */
  625.         size_t  offset;     /* field offset */
  626.         size_t  size;       /* size of field */
  627.         int     type;       /* type of field */
  628.         int     flags;      /* flags */
  629.         char *  filename;   /* index file name */
  630.     } cbfield_t;
  631.  
  632. offset is the location of the field within the record and size is the
  633. size of the field.  type specifies the field data type, legal values for
  634. which are shown in Table 4.1; the user can also define new data types
  635. (see Appendix B).  flags values are constructed by bitwise ORing together
  636. flags from the following list.
  637.  
  638.     CB_FKEY         Field is to be a key.
  639.     CB_FUNIQ        Only for use with CB_FKEY.
  640.                     Indicates that the key is
  641.                     constrained to be unique.
  642.  
  643. If CB_FKEY is set, filename must point to the name of the file containing
  644. the index.
  645.  
  646.     t_char      signed character
  647.     t_charv     signed character array
  648.     t_uchar     unsigned character
  649.     t_ucharv    unsigned character array
  650.     t_short     signed short integer
  651.     t_shortv    signed short integer array
  652.     t_ushort    unsigned short integer
  653.     t_ushortv   unsigned short integer array
  654.     t_int       signed integer
  655.     t_intv      signed integer array
  656.     t_uint      unsigned integer
  657.     t_uintv     unsigned integer array
  658.     t_long      signed long integer
  659.     t_longv     signed long integer array
  660.     t_ulong     unsigned long integer
  661.     t_ulongv    unsigned long integer array
  662.     t_float     floating point
  663.     t_floatv    floating point array
  664.     t_double    double precision
  665.     t_doublev   double precision array
  666.     t_ldouble   long double
  667.     t_ldoublev  long double array
  668.     t_pointer   pointer
  669.     t_string    character string
  670.     t_cistring  case-insensitive character string
  671.     t_binary    block of binary data (e.g., graphics)
  672.  
  673.                       Table 4.1. cbase Data Types
  674.  
  675.      If it is necessary for the application to directly manipulate the
  676. field list, the code to do so should be considered non-portable and
  677. isolated, since future enhancements to cbase may require that the
  678. structure of the field list be altered.
  679.  
  680.      Before an existing cbase can be accessed, it must be opened.  This
  681. is done with the function
  682.  
  683.     cbase_t *cbopen(const char *cbname, const char *type,
  684.                                     int fldc, const cbfield_t fldv[]);
  685.  
  686. cbname, fldc, and fldv are the same as for cbcreate, and must be given
  687. the same values as when the cbase was created.  type points to a
  688. character string specifying the type of access for which the cbase is to
  689. be opened (as for the stdio function fopen).  Legal values for type are
  690.  
  691.     "r"         open for reading
  692.     "r+"        open for update (reading and writing)
  693.  
  694. cbopen returns a pointer to the open cbase.
  695.  
  696.      The cbsync function causes any buffered data for a cbase to be
  697. written out.
  698.  
  699.     int cbsync(cbase_t *cbp);
  700.  
  701. The cbase remains open and the buffers retain their contents.
  702.  
  703.      After processing is completed on an open cbase, it must be closed
  704. using the function
  705.  
  706.     int cbclose(cbase_t *cbp);
  707.  
  708. The cbclose function causes any buffered data for the cbase to be written
  709. out, unlocks it, closes it, and frees the cbase pointer.
  710.  
  711.  
  712. 4.2  Lock Functions
  713.  
  714.      Before an open cbase can be accessed, it must be locked in order to
  715. prevent possible conflicts arising from two processes attempting to
  716. access the same data simultaneously.  The function used to control the
  717. lock status of a cbase is
  718.  
  719.     int cblock(cbase_t *cbp, int ltype);
  720.  
  721. where cbp is a pointer to an open cbase and ltype is the lock type to be
  722. placed on the cbase.  The legal values for ltype are
  723.  
  724.     CB_RDLCK    lock cbase for reading
  725.     CB_WRLCK    lock cbase for reading and writing
  726.     CB_RDLKW    lock cbase for reading (wait)
  727.     CB_WRLKW    lock cbase for reading and writing (wait)
  728.     CB_UNLCK    unlock cbase
  729.  
  730. If ltype is CB_RDLCK and the cbase is currently write locked by another
  731. process, or if ltype is CB_WRLCK and the cbase is currently read or write
  732. locked by another process, cblock will fail and set errno to EAGAIN.  Any
  733. number of processes can have a cbase simultaneously read locked.  For the
  734. wait lock types, cblock will not return until the lock is available.
  735.  
  736.      The cbgetlck function reports the lock status held by the calling
  737. process on a cbase.
  738.  
  739.      int cbgetlck(cbase_t *cbp);
  740.  
  741. It returns one of the legal values for the ltype argument in the cblock
  742. function.
  743.  
  744.  
  745. 4.3  Record Cursor Position Functions
  746.  
  747.      Each open cbase has a record cursor.  At any given time the record
  748. cursor is positioned either on a record in that cbase or on a special
  749. position called null.  The record on which the cursor is located is
  750. referred to as the current record.  The operations performed by most
  751. cbase functions are either on or relative to the current record, so the
  752. initial step in a transaction on a cbase is usually to position the
  753. record cursor on the desired record.
  754.  
  755.      When accessing the records in a cbase in the order that they are
  756. stored, the following functions are used to move the record cursor.
  757.  
  758.     int cbrecfirst(cbase_t *cbp);
  759.     int cbreclast(cbase_t *cbp);
  760.     int cbrecnext(cbase_t *cbp);
  761.     int cbrecprev(cbase_t *cbp);
  762.  
  763. The cbrecfirst function positions the record cursor to the first record,
  764. and cbreclast to the last record.  Before calling either of these
  765. functions cbreccnt should be used to test if the cbase is empty.
  766.  
  767.     unsigned long cbreccnt(cbase_t *cbp);
  768.  
  769. If the cbase is empty, there is no first or last record and so these
  770. functions would return an error.  The cbrecnext function advances the
  771. record cursor to the succeeding record, and cbrecprev retreats it to the
  772. preceding record.  In the record ordering, null is located before the
  773. first record and after the last.
  774.  
  775.      There are also functions for saving the current position of the
  776. record cursor and resetting it to that position.
  777.  
  778.     int cbgetrcur(cbase_t *cbp, cbrpos_t *cbrposp);
  779.     int cbsetrcur(cbase_t *cbp, const cbrpos_t*cbrposp);
  780.  
  781. The cbgetrcur function gets the current position of the record cursor and
  782. saves it in the variable pointed to by cbrposp.  cbrpos_t is the cbase
  783. record position type, defined in <cbase.h>.  cbsetrcur can then be used
  784. later to set the record cursor back to that position.  The record cursor
  785. can be positioned on null by passing cbsetrcur the NULL pointer rather
  786. than a pointer to a variable.  Other than this special case, cbsetrcur
  787. should only be called with record cursor positions previously saved with
  788. cbgetrcur.  Also, a record position should not be considered valid if the
  789. cbase has been unlocked at any time since it was obtained.  This is
  790. because the database may have been altered by another process during that
  791. time, and the record at that position may have been deleted, and the
  792. location possible reused for a new record.  To reposition to a record
  793. after the cbase has been unlocked, a search on a unique key should be
  794. used.
  795.  
  796.      The cbrcursor macro is used to test if the record cursor for a cbase
  797. is positioned on a record or on null.
  798.  
  799.     void *cbrcursor(cbase_t *cbp);
  800.  
  801. If the record cursor of the cbase pointed to by cbp is positioned on
  802. null, cbrcursor returns the NULL pointer.  If it is on a record,
  803. cbrcursor returns a value not equal to the NULL pointer.  This function
  804. is useful for loops needing to test when the last (or first) record has
  805. been reached. 
  806.  
  807.      The cbrecalign function aligns the record cursor with a specified
  808. key cursor.
  809.  
  810.     int cbrecalign(cbase_t *cbp, int field);
  811.  
  812. field is the key with which to align the record cursor.  The relationship
  813. between the key cursors and the record cursor is explained in the next
  814. section.
  815.  
  816.      Whether or not any the order of the records in a cbase has any
  817. significance is totally up to the applications.
  818.  
  819.  
  820. 4.4  Key Cursor Position Functions
  821.  
  822.      In addition to a record cursor, each open cbase also has a key
  823. cursor for each key defined for that cbase.  Like the record cursor, a
  824. key cursor is positioned either on a record in that cbase or on null. 
  825. To access a cbase in the sort order of a certain key, the appropriate key
  826. cursor is used instead of the record cursor.  Each key cursor moves
  827. independently of the others, but whenever a key cursor position is set,
  828. the record cursor is moved to the same record.  The key cursors are not
  829. affected by moving the record cursor.
  830.  
  831.      The following functions are used to move a key cursor.
  832.  
  833.     int cbkeyfirst(cbase_t *cbp, int field);
  834.     int cbkeylast(cbase_t *cbp, int field);
  835.     int cbkeynext(cbase_t *cbp, int field);
  836.     int cbkeyprev(cbase_t *cbp, int field);
  837.  
  838. These perform as do the corresponding functions for the record cursor,
  839. and the same rules concerning locking apply.  Note that the key cursor
  840. functions can be used only with fields defined to be keys (see cbcreate
  841. in section 4.1).
  842.  
  843.      The following function is used to search for a key of a certain 
  844. value.
  845.  
  846.     int cbkeysrch(cbase_t *cbp, int field, const void *buf);
  847.  
  848. field is the key to search for the data item pointed to by buf.  If the
  849. key is found, 1 is returned and the key and record cursors positioned to
  850. the record having that key.  If there is no record with that key, 0 is
  851. returned and the key and record cursor positioned to the record (possibly
  852. null) that would follow a record with that key value.
  853.  
  854.      Since the key cursors do not automatically follow the record cursor,
  855. the situation sometimes occurs where the record cursor is positioned to
  856. the desired record, but the cursor for the key to be used next is not. 
  857. The cbkeyalign function is used to align a specified key cursor with the
  858. record cursor.
  859.  
  860.     int cbkeyalign(cbase_t *cbp, int field);
  861.  
  862. The reason the key cursors are not updated every time the record cursor
  863. moves is not because it would be in any way difficult to do so, but
  864. because this would increase the overhead enormously.  And since only one
  865. key cursor is normally used at a time, this extra overhead would almost
  866. never provide any benefit in return.
  867.  
  868.      As for the record cursor, each key cursor position can be tested to
  869. be positioned on a record or on null.
  870.  
  871.     void *cbkcursor(cbase_t *cbp, int field);
  872.  
  873. If the key cursor specified by field of the cbase pointed to by cbp is
  874. positioned on null, cbkcursor returns the NULL pointer.  If it is on a
  875. record, cbkcursor returns a value not equal to the NULL pointer.
  876.  
  877.  
  878. 4.5  Input/Output Functions
  879.  
  880.      To read a record from a cbase, the record cursor for that cbase is
  881. first positioned to the desired record using either the record cursor
  882. position functions or the key cursor position functions.  One of the
  883. following functions is then called to read from the current record.
  884.  
  885.     int cbgetr(cbase_t *cbp, void *buf);
  886.     int cbgetrf(cbase_t *cbp, int field, void *buf);
  887.  
  888. cbp is a pointer to an open cbase and buf points to the storage area to
  889. receive the data read from the cbase.  The cbgetr function reads the
  890. entire current record, while cbgetrf reads the specified field from the
  891. current record.
  892.  
  893.      The function for inserting a new record into a cbase is
  894.  
  895.     int cbinsert(cbase_t *cbp, const void *buf);
  896.  
  897. where buf points to the record to be inserted.  When a new record is
  898. inserted into a cbase, the position it holds relative to each key cursor
  899. is defined by the sort order for that key field.  There is no predefined
  900. sort order associated with the record cursor, however, and it is up to
  901. the user whether or not to store the records for each cbase in a sorted
  902. or unsorted order.  To store records in a sorted order, the record cursor
  903. is first positioned to the record after which to insert the new record. 
  904. cbinsert is then called to insert the record pointed to by buf after the
  905. current record.  If no sort order is desired, the step to position the
  906. record cursor is skipped, resulting in the record being inserted
  907. following whatever location the record cursor happens to be positioned.
  908.  
  909.      The cbdelcur function is used to delete a record.
  910.  
  911.     int cbdelcur(cbase_t *cbp);
  912.  
  913. The record cursor must first be positioned on the record to delete, then
  914. cbdelcur called to delete the current record.  cbdelcur sets the record
  915. cursor to null.
  916.  
  917.      The cbputr function writes over an existing record.
  918.  
  919.     int cbputr(cbase_t *cbp, const void *buf);
  920.  
  921. buf points to the new record contents.  Writing over an existing record
  922. is equivalent to deleting the record and inserting a new one in the same
  923. position in the file.  If the new record contains an illegal duplicate
  924. key, this will cause the insert to fail, resulting in the record having
  925. been deleted from the cbase.  The exact behavior that a program should
  926. have in such a circumstance is different for different applications, and
  927. so it is usually desirable to use cbdelcur and cbinsert directly rather
  928. than cbputr.
  929.  
  930.  
  931. 4.6  Import/Export Functions
  932.  
  933.      cbase data can be exported to a text file using the cbexport
  934. function.
  935.  
  936.     int cbexport(cbase_t *cbp, const char *filename);
  937.  
  938. Every record in cbase cbp is converted to a text format and written to
  939. the file filename.  The export file format is defined as follows.
  940.  
  941.     - Each record is terminated by a newline ('\n').
  942.     - The fields in a record are delimited by vertical
  943.       bars ('|').
  944.     - Each field contains only printable characters.
  945.     - If a field contains the field delimiter
  946.       character, that character is replaced with \F.
  947.     - The individual elements of array data types are
  948.       exported as individual fields.
  949.  
  950.      Data may be imported from a text file using the cbimport function.
  951.  
  952.     int cbimport(cbase_t *cbp, const char *filename);
  953.  
  954. cbimport reads each record from the text file filename and inserts it
  955. into the cbase cbp.  If cbimport encounters a record containing an
  956. illegal duplicate key, that record is skipped and the import continues
  957. on normally, but a value of -1 is returned with errno set to CBEDUP to
  958. notify the application that one or more records were skipped.  It is up
  959. to the application whether or not to treat this as a true error.
  960.  
  961.      Data import/export is primarily used to move data between different
  962. database formats.  This sometimes requires some slight rearranging of the
  963. text before importing.  One common tool designed for just this sort of
  964. task is a awk.  Awk comes standard with UNIX, and is becoming available
  965. for most other systems, as well.  There are a few freeware versions of
  966. awk for DOS -- look for these on the Citadel BBS.
  967.  
  968.      Figure 4.1 shows an awk program for inserting a new field at
  969. position two in all the records in a text file (note that awk field
  970. numbering starts at one, not zero).  The predefined variables FS and OFS
  971. are used to set the input and output field separators, respectively.  The
  972. predefined variables RS and ORS are used to set the input and output
  973. record separators, respectively.  Setting these variables appropriately
  974. is all that is necessary to convert between text file formats using
  975. different field and record separators.  The awk program in figure 4.2
  976. converts text files exported from a database using the tab character as
  977. a field separator to a format for import by cbase.
  978.  
  979. BEGIN {
  980.     # set input and output field and record separators
  981.     FS  = "|";
  982.     OFS = FS;
  983.     RS  = "\n";
  984.     ORS = RS;
  985.     NEWFIELD = 2;       # field to insert
  986. }
  987.  
  988. # insfld:  insert field n of current record
  989. function insfld(n)
  990. {
  991.     if (n < 1 || n > NF + 1) {
  992.         return -1;
  993.     }
  994.  
  995.     for (i = NF; i >= n; --i) {
  996.         $(i + 1) = $i;
  997.     }
  998.     $n = "";
  999.  
  1000.     return 0;
  1001. }
  1002.  
  1003. {
  1004.     # insert a new field in each record then print
  1005.     if (insfld(NEWFIELD) == -1) {
  1006.         printf "Error inserting new field %d.\n", NEWFIELD;
  1007.         exit 1;
  1008.     }
  1009.     print $0;
  1010. }
  1011.  
  1012. END {
  1013.     exit 0;
  1014. }
  1015.  
  1016.              Figure 4.1. awk Program to Insert a New Field
  1017.  
  1018.  
  1019. BEGIN {
  1020.     # set input and output field and record separators
  1021.     FS  = "\t";
  1022.     OFS = "|";
  1023.     RS  = "\n";
  1024.     ORS = RS;
  1025. }
  1026.  
  1027. {
  1028.     # print each line with new separators
  1029.     print $0
  1030. }
  1031.  
  1032. END {
  1033.     exit 0;
  1034. }
  1035.  
  1036.        Figure 4.2. awk Program to Change Field/Record Separators
  1037.  
  1038.  
  1039.  
  1040.  
  1041.                                             Chapter 5:  Custom Indexing
  1042.  
  1043.  
  1044.      cbase automatically handles indexes on a single complete field.  In
  1045. some instances, however, it is necessary to index on a combination of
  1046. fields (i.e., compound keys), partial fields, or even data derived from
  1047. but not actually stored in a record.  Because of the layered design of
  1048. cbase, the btree library can be accessed directly by the application to
  1049. maintain virtually any type of index, not necessarily for a cbase
  1050. database.
  1051.  
  1052.      The btree interface is very similar to that for cbase (most cbase
  1053. key functions simply call a btree function for a specified index), and
  1054. the reader is referred to the btree section of the reference manual for
  1055. most of the details on the usage of this library.
  1056.  
  1057.      The btcreate function to create a btree is of fundamental
  1058. importance, since here is where the btree is actually defined, and a
  1059. brief discussion of this is given below with a typical example.
  1060.  
  1061.     int btcreate(const char *filename, int m, size_t keysize, int fldc,
  1062.                                                const btfield_t fldv[]);
  1063.  
  1064. The most apparent difference from cbcreate is the extra parameter m.  m
  1065. specifies the order of the btree to be created.  The order is the maximum
  1066. number of children that a node in the tree can have.  The order to be
  1067. used depends on several factors such as the key size, but a value of
  1068. around 10 will normally serve fairly well if the user does not wish to
  1069. get into btree internals.  The advanced user who wishes to fine-tune an
  1070. index is referred to COME79 and HORO76.
  1071.  
  1072.      The filename, keysize, and field count all function just as for
  1073. cbcreate.  The field list is the same in principle and has a similar
  1074. structure.
  1075.  
  1076.     typedef struct {
  1077.         size_t  offset;     /* offset of field in key */
  1078.         size_t  len;        /* field length */
  1079.         int     (*cmp)(const void *p1, const void *p2, size_t n);
  1080.                             /* comparison function */
  1081.         int     flags;      /* flags */
  1082.     } btfield_t;
  1083.  
  1084. offset, len, cmp, and flags are all the same as for cbcreate.  Valid
  1085. btree field flags are
  1086.  
  1087.     BT_FASC     ascending order
  1088.     BT_FDSC     descending order
  1089.  
  1090. The fields in fldv must be ordered from the major sort first to the most
  1091. minor sort last.
  1092.  
  1093.      The logical organization of cbase indexes is referred to by the term
  1094. inverted file.  An inverted file is quite simply a table of sorted keys
  1095. each paired with a pointer to the record containing that key; note that
  1096. the term inverted file does not imply any specific file structure (i.e.,
  1097. B-tree, hash, etc.).  A book index is an inverted file where words (keys)
  1098. from the text (database) are each paired with a page number (pointer) to
  1099. the page (record) containing that word.  In a cbase inverted file, the
  1100. pointer is a cbase record position, whose type cbrpos_t is defined in
  1101. <cbase.h>.  The last member of a btree key structure for a cbase index
  1102. is a cbase record position.
  1103.  
  1104.      B-trees by nature do not allow duplicate keys.  But for an inverted
  1105. file the key in combination with the record position will always be
  1106. unique, thus the effect of duplicate keys can be produced by including
  1107. the record position as the most minor sort field.  To constrain a key to
  1108. be unique, simply leave the record position out of the field list.  A
  1109. comparison function cbrposcmp for cbase record positions is included in
  1110. the cbase library.  Whenever a custom index is being maintained for a
  1111. cbase, care must be taken to update the index in parallel with the cbase
  1112. to prevent them getting out of sync.  The record position in the key is
  1113. obtained with cbgetrcur.
  1114.  
  1115.      The code fragment below shows how a compound key allowing duplicates
  1116. for a name stored as separate fields.  The example program in the next
  1117. chapter will handle names without compound keys.  Note that the fields
  1118. in the key are actually stored first-middle-last to facilitate copying
  1119. from the record structure.  It is the order in the field list which
  1120. defined the relative sort precedences of the fields.
  1121.  
  1122.     record name {               /* DDL record definition */
  1123.         t_string    nm_first[12];
  1124.         t_string    nm_mi[1];
  1125.         t_string    nm_last[12];
  1126.         t_string    nm_addr[80];
  1127.     };
  1128.  
  1129.     struct lfmkey {
  1130.         char        first[12];
  1131.         char        mi[1];
  1132.         char        last[12];
  1133.         cbrpos_t    rpos;
  1134.     };
  1135.  
  1136.     btfield_t lfmfldv[] = {     /* last name first index */
  1137.         {
  1138.             offsetof(struct lfmkey, last),
  1139.             sizeofm(struct lfmkey, last),
  1140.             strncmp,
  1141.             BT_FASC,
  1142.         },
  1143.         {
  1144.             offsetof(struct lfmkey, first),
  1145.             sizeofm(struct lfmkey, first),
  1146.             strncmp,
  1147.             BT_FASC,
  1148.         },
  1149.         {
  1150.             offsetof(struct lfmkey, mi),
  1151.             sizeofm(struct lfmkey, mi),
  1152.             strncmp,
  1153.             BT_FASC,
  1154.         },
  1155.         {
  1156.             sizeof(struct lfmkey),
  1157.             sizeofm(struct lfmkey, rpos),
  1158.             cbrposcmp,
  1159.             BT_FASC,
  1160.         },
  1161.     };
  1162.  
  1163.     #define LFMFLDC (nelems(lfmfldv))
  1164.  
  1165.  
  1166.  
  1167.  
  1168.                                          Chapter 6:  An Example Program
  1169.  
  1170.  
  1171.      Included with cbase is rolodeck, an complete example program
  1172. illustrating the use of cbase.  Rolodeck is a program for storing
  1173. business cards.  To allow it to be compiled without requiring any
  1174. additional libraries for displays, and because the purpose of the program
  1175. is purely instructional, the program has been given only a simple
  1176. scrolling user interface.  The source for rolodeck is included with
  1177. cbase.
  1178.  
  1179.      Prior to performing any database operations, provision must be made
  1180. to flush any buffered on termination of the application.  This is done
  1181. by registering the blkio function bcloseall to be automatically called
  1182. on exit.
  1183.  
  1184.     /* register termination function to flush database buffers */
  1185.     if (atexit(bcloseall)) {
  1186.         perror("atexit");
  1187.         exit(EXIT_FAILURE);
  1188.     }
  1189.  
  1190. If atexit is not available, bexit must be used everywhere in place of
  1191. exit.
  1192.  
  1193.      Rolodeck uses a simple index for names by storing the name in a
  1194. single field last-name-first.  To allow the user to input names
  1195. first-name-first and to display names in the same manner, the following
  1196. two functions are used to convert between the two formats.
  1197.  
  1198.     int fmltolfm(char *t, const char *s, size_t n);
  1199.     int lfmtofml(char *t, const char *s, size_t n);
  1200.  
  1201. Another support function, cvtss, is also used throughout rolodeck to
  1202. perform string conversions such as removing white space.  See the
  1203. respective reference manual entries for more information on each of these
  1204. functions.
  1205.  
  1206.  
  1207. 6.1  Data Definition
  1208.  
  1209.      The first step in writing a program using cbase is to define the
  1210. data to be stored.  This should be done in a separate header file for
  1211. each record type to be used.  Figure 6.1 lists rolodeck.ddl, the data
  1212. definition for the business card record type used by the rolodeck
  1213. program.  Note that there is no need to store the terminating nul
  1214. character for string data, unless, of course, it is shorter than the
  1215. field size.
  1216.  
  1217.    Figure 6.2 lists the database definition header generated from
  1218. rolodeck.ddl by cbddlp.  The macro H_ROLODECK tested then defined at the
  1219. top is simply to prevent the header from being processed more than once
  1220. if included multiple times in the same module.  The contents of the file
  1221. are the cbase name ROLODECK, the C struct rolodeck for manipulating
  1222. record in memory, the field macros RD_*, and the field count and list
  1223. RDFLDC and rdfldv, all generated as described in Chapter 3.  The include
  1224. file rolodeck.i should be included in the main rolodeck module.  Details
  1225. of its contents are normally or no concern to the application.
  1226. /* constants */
  1227. #define NAME_MAX    (40)    /* maximum name length */
  1228. #define ADDR_MAX    (40)    /* maximum address length */
  1229. #define NOTELIN_MAX (4)     /* note lines */
  1230. #define NOTECOL_MAX (40)    /* note columns */
  1231.  
  1232. /* file assignments */
  1233. data  file "rolodeck.dat" contains rolodeck;
  1234. index file "rdcont.ndx"   contains rd_contact;
  1235. index file "rdcomp.ndx"   contains rd_company;
  1236.  
  1237. /* record definitions */
  1238. record rolodeck {                      /* rolodeck record */
  1239.     unique key t_string
  1240.                  rd_contact[NAME_MAX]; /* contact name */
  1241.     t_string     rd_title[40];         /* contact title */
  1242.     key t_string rd_company[NAME_MAX]; /* company name */
  1243.     t_string     rd_addr[ADDR_MAX];    /* address */
  1244.     t_string     rd_city[25];          /* city */
  1245.     t_string     rd_state[2];          /* state */
  1246.     t_string     rd_zip[10];           /* zip code */
  1247.     t_string     rd_phone[12];         /* phone number */
  1248.     t_string     rd_ext[4];            /* phone extension */
  1249.     t_string     rd_fax[12];           /* fax number */
  1250.     t_string     rd_notes[NOTELIN_MAX * NOTECOL_MAX];
  1251.                                        /* notes */
  1252. };
  1253.  
  1254.            Figure 6.1.  Definition of the Rolodeck Database
  1255.  
  1256.  
  1257. #ifndef H_ROLODECK
  1258. #define H_ROLODECK
  1259.  
  1260. /* libray headers */
  1261. #include <cbase.h>
  1262.  
  1263. #define NAME_MAX    (40)    /* maximum name length */
  1264. #define ADDR_MAX    (40)    /* maximum address length */
  1265. #define NOTELIN_MAX (4)     /* note lines */
  1266. #define NOTECOL_MAX (40)    /* note columns */
  1267.  
  1268. /* record name */
  1269. #define ROLODECK    "rolodeck.dat"
  1270.  
  1271. /* rolodeck record definition */
  1272. typedef struct rolodeck {
  1273.     char rd_contact[NAME_MAX];
  1274.     char rd_title[40];
  1275.     char rd_company[NAME_MAX];
  1276.     char rd_addr[ADDR_MAX];
  1277.     char rd_city[25];
  1278.     char rd_state[2];
  1279.     char rd_zip[10];
  1280.     char rd_phone[12];
  1281.     char rd_ext[4];
  1282.     char rd_fax[12];
  1283.     char rd_notes[NOTELIN_MAX * NOTECOL_MAX];
  1284. } rolodeck_t;
  1285.  
  1286. /* field names for record rolodeck */
  1287. #define RD_CONTACT  (0)
  1288. #define RD_TITLE    (1)
  1289. #define RD_COMPANY  (2)
  1290. #define RD_ADDR     (3)
  1291. #define RD_CITY     (4)
  1292. #define RD_STATE    (5)
  1293. #define RD_ZIP      (6)
  1294. #define RD_PHONE    (7)
  1295. #define RD_EXT      (8)
  1296. #define RD_FAX      (9)
  1297. #define RD_NOTES    (10)
  1298. #define RDFLDC      (11)
  1299.  
  1300. /* field definition list for record rolodeck */
  1301. extern cbfield_t rdfldv[RDFLDC];
  1302.  
  1303. #endif
  1304.  
  1305.                Figure 6.2. Rolodeck Database Header File
  1306.      It should be noted that every record type should normally have at
  1307. least one unique key field that can be used to uniquely identify records. 
  1308. As mentioned in Section 4.3, the physical record position cannot be
  1309. relied upon after the cbase has been unlocked.
  1310.  
  1311.  
  1312. 6.2  Opening a cbase
  1313.  
  1314.      The first step in accessing an existing cbase is to open it.  Figure
  1315. 6.3 shows the code from rolodeck.c to open the rolodeck cbase.  rolodeck
  1316. is opened with a type argument of "r+" to allow both reading and writing. 
  1317. The other arguments are the cbase name, ROLODECK, the field count,
  1318. RDFLDC, and the field definition list, rdfldv, all defined in the data
  1319. definition header file, rolodeck.h.  On error cbopen returns the NULL
  1320. pointer.  For this program there is only one cbase, but most applications
  1321. will have more.
  1322.  
  1323.      If the named cbase does not exist, cbopen will fail and set errno
  1324. to ENOENT.  In this example, if the rolodeck cbase does not exist, it is
  1325. created and the program continues as normal.  Note that the cbase must
  1326. still be opened after it is created.  In some cases a separate program
  1327. is written to create all the cbases required by an application, in which
  1328. case the main program would interpret ENOENT as an error and exit.
  1329.  
  1330. /* open rolodeck cbase */
  1331. cbp = cbopen(ROLODECK, "r+", RDFLDC, rdfldv);
  1332. if (cbp == NULL) {
  1333.     if (errno != ENOENT) {
  1334.         fprintf(stderr, "cbopen:  error %d.\n", errno);
  1335.         exit(EXIT_FAILURE);
  1336.     }
  1337.     /* create rolodeck cbase */
  1338.     puts("Rolodeck does not exist.  Creating...");
  1339.     if (cbcreate(ROLODECK, sizeof(struct rolodeck), RDFLDC, rdfldv) ==
  1340. -1) {
  1341.         fprintf(stderr, "cbcreate:  error %d.\n", errno);
  1342.         exit(EXIT_FAILURE);
  1343.     }
  1344.     cbp = cbopen(ROLODECK, "r+", RDFLDC, rdfldv);
  1345.     if (cbp == NULL) {
  1346.         fprintf(stderr, "cbopen:  error %d.\n", errno);
  1347.         exit(EXIT_FAILURE);
  1348.     }
  1349. }
  1350.  
  1351.                       Figure 6.3. Opening a cbase
  1352.  
  1353.  
  1354. 6.3  Locking a cbase
  1355.  
  1356.      Before accessing an open cbase, it must first be locked.  If data
  1357. is to be written to the cbase, it must be write locked, otherwise only
  1358. a read lock is required.  A cbase can be read locked by more than one
  1359. process at the same time, and read locks are therefore also called shared
  1360. locks.  A write lock, on the other hand, is an exclusive lock; a write
  1361. locked cbase can be neither read nor write locked by any other process. 
  1362. Write locks are exclusive because, if one process tried to read data
  1363. while it was partially modified by another, the data would probably be
  1364. in an inconsistent state.  Processes that will only read data, however,
  1365. can safely do so concurrently.
  1366.  
  1367.      While a cbase is write locked, other processes needing to access
  1368. that cbase must wait until it is unlocked so that they can in turn lock
  1369. it themselves to complete their processing.  While a cbase is read
  1370. locked, only processes needing to write must wait.  Using a write lock
  1371. when a read lock would suffice will therefore delay other processes
  1372. unnecessarily.  Locks of either type should be held for the shortest time
  1373. possible; a common mistake in writing multiuser applications is to pause
  1374. for use input while holding a lock, causing that lock to be held
  1375. indefinitely.
  1376.  
  1377.      If an attempt is made to obtain a lock on a cbase, but is blocked
  1378. by a lock held by another process, cblock will fail and set errno to
  1379. EAGAIN.  The call to cblock is therefore usually made in a loop with a
  1380. predefined maximum number of tries.  It is convenient to place this in
  1381. a function configured for the application being developed.  Figure 6.4
  1382. shows this function from rolodeck.c.  It may also be suitable in some
  1383. instances to sleep for a short (possibly random) time between attempts
  1384. to lock.
  1385.  
  1386. #define LCKTRIES_MAX (50)    /* max lock tries */
  1387.  
  1388. /* rdlock:  rolodeck lock */
  1389. int rdlock(cbase_t *cbp, int ltype)
  1390. {
  1391.     int i = 0;
  1392.  
  1393.     for (i = 0; i < LCKTRIES_MAX; ++i) {
  1394.         if (cblock(cbp, ltype) == -1) {
  1395.             if (errno == EAGAIN) {
  1396.                 continue;
  1397.             }
  1398.             return -1;
  1399.         } else {
  1400.             return 0;
  1401.         }
  1402.     }
  1403.  
  1404.     errno = EAGAIN;
  1405.     return -1;
  1406. }
  1407.  
  1408.                  Figure 6.4. Rolodeck Locking Function
  1409.  
  1410.      There are also two lock types (CB_RDLKW and CB_WRLKW) which, if the
  1411. requested lock is blocked, will wait until it can be obtained.  These are
  1412. not usually used, however, because if the lock does not become free in
  1413. a reasonable time, the process waiting for the lock will be hung.
  1414.  
  1415.      For applications where there will be only a single process accessing
  1416. the database, the necessary locks can be set immediately after opening
  1417. the cbases to be accessed and left locked.
  1418.  
  1419.      One critical concern when locking multiple cbases is the possibility
  1420. of deadlock.  Deadlock is an extensive subject, and there are a number
  1421. of ways of dealing with it.  Most texts on operating systems (see CALI82)
  1422. and database theory cover the subject in detail.
  1423.  
  1424.  
  1425. 6.4  Accessing a cbase
  1426.  
  1427.      The gross structure of the rolodeck program is a case statement
  1428. within a loop.  At the start of the loop a user request is read and used
  1429. to select the action performed in the case statement.  Each individual
  1430. action performed in the case statement illustrates the use of cbase to
  1431. perform a basic operation, e.g., inserting a record, deleting a record,
  1432. finding the next record, exporting data to a text file, etc.  The
  1433. operation of finding the next record serves as a good general example. 
  1434. The code for this from rolodeck.c is shown in figure 6.5.
  1435.  
  1436.      One of the most important points to notice in the example code is
  1437. that a unique key (the contact name, here) rather than a saved record
  1438. position is used to relocate the current record when a cbase is locked. 
  1439. Because of this, cbsetrpos cannot be used with a record position obtained
  1440. during a previously held lock.
  1441.  
  1442.      Another central point is the use of multiple keys.  In the rolodeck
  1443. program, both the contact and the company names are keys.  A variable sf
  1444. is used in rolodeck.c to identify the current sort field, which can be
  1445. changed interactively.  Before using the cbkeynext function, the
  1446. appropriate key cursor must first be positioned.  cbkeysrch positions
  1447. only the key being searched, here being the unique key.  If the next card
  1448. is to be found using the sort order of a different key, cbkeyalign must
  1449. first be used to align that key cursor with the current record.
  1450.  
  1451. case REQ_NEXT_CARD:     /* next card */
  1452.     rdlock(cbp, CB_RDLCK);
  1453.     if (cbreccnt(cbp) == 0) {
  1454.         printf("The rolodeck is empty.\n\n");
  1455.         rdlock(cbp, CB_UNLCK);
  1456.         continue;
  1457.     }
  1458.     /* use unique key field to set rec cursor */
  1459.     found = cbkeysrch(cbp,RD_CONTACT, rd.rd_contact);
  1460.     if (sf != RD_CONTACT) {
  1461.         /* align cursor of sort key */
  1462.         cbkeyalign(cbp, sf);
  1463.     }
  1464.     if (found == 1) {
  1465.         /* advance key (and rec) cursor 1 pos */
  1466.         cbkeynext(cbp, sf);
  1467.     }
  1468.     if (cbrcursor(cbp) == NULL) {
  1469.         printf("End of deck.\n\n");
  1470.         rdlock(cbp, CB_UNLCK);
  1471.         continue;
  1472.     }
  1473.     cbgetr(cbp, &rd);
  1474.     rdlock(cbp, CB_UNLCK);
  1475.     break;
  1476.  
  1477.                    Figure 6.5. Next Rolodeck Record
  1478.  
  1479.  
  1480. 6.5  Closing a cbase
  1481.  
  1482.      When a program is through accessing a cbase, the cbase should be
  1483. closed.  Figure 6.6 shows this code from rolodeck.c.
  1484.  
  1485.     /* close cbase */
  1486.     if (cbclose(cbp) == -1) {
  1487.         fprintf(stderr, "cbclose:  error %d.\n", errno);
  1488.         bexit(EXIT_FAILURE);
  1489.     }
  1490.  
  1491.                       Figure 6.6. Closing a cbase
  1492.  
  1493. A cbase is automatically unlocked when it is closed.
  1494.  
  1495.  
  1496. 6.6  Storing Variable Length Text
  1497.  
  1498.      The example database of this chapter has a free-form text field for
  1499. storing notes, the length of which is fixed at four lines.  For this
  1500. application a fixed-length design is not inappropriate, but in many
  1501. instances a database must be able to handle text without length
  1502. restrictions.  A bulletin board message system is an example of this.
  1503.  
  1504.      This problem is easily addressed by organizing the text as a
  1505. collection of line records rather than as a single block of text.  In
  1506. addition to the text itself, each line record would contain an number
  1507. identifying the block of text to which it belongs, and the number of the
  1508. line in that text block.  Figure 6.7 shows a modified definition for the
  1509. rolodeck database that uses variable-length notes.
  1510.  
  1511. /* constants */
  1512. #define NAME_MAX    (40)    /* name length max */
  1513. #define ADDR_MAX    (40)    /* address length max */
  1514. #define LINLEN_MAX  (40)    /* line length max */
  1515.  
  1516. /* file assignments */
  1517. data  file "rolodeck.dat" contains rolodeck;
  1518. index file "rdcont.ndx"   contains rd_contact;
  1519. index file "rdcomp.ndx"   contains rd_company;
  1520.  
  1521. /* record definitions */
  1522. record rolodeck {                      /* rolodeck record */
  1523.     unique key t_string
  1524.                  rd_contact[NAME_MAX]; /* contact name */
  1525.     t_string     rd_title[40];         /* contact title */
  1526.     key t_string rd_company[NAME_MAX]; /* company name */
  1527.     t_string     rd_addr[ADDR_MAX];    /* address */
  1528.     t_string     rd_city[25];          /* city */
  1529.     t_string     rd_state[2];          /* state */
  1530.     t_string     rd_zip[10];           /* zip code */
  1531.     t_string     rd_phone[12];         /* phone number */
  1532.     t_string     rd_ext[4];            /* phone extension */
  1533.     t_string     rd_fax[12];           /* fax number */
  1534.     t_int        rd_notes;             /* notes */
  1535. };
  1536.  
  1537. record text {                          /* text record */
  1538.     t_int        tx_textno;            /* text number */
  1539.     t_uchar      tx_lineno;            /* line number */
  1540.     t_string     tx_line[LINLEN_MAX];  /* line of text */
  1541. };
  1542.  
  1543.       Figure 6.7.  A Rolodeck Database with Variable Length Text
  1544.  
  1545.      In this new rolodeck database, only an integer tag identifying the
  1546. note is stored in the rolodeck record.  The actual text is retrieved
  1547. using the following compound key.
  1548.  
  1549.     struct textkey {
  1550.         int             textno;
  1551.         unsigned char   lineno;
  1552.         cbrpos_t        rpos;
  1553.     };
  1554.  
  1555. textno is the major sort field and lineno the minor sort.  The key must
  1556. be unique, so the record position should not be included as a sort field
  1557. (see Chapter 5).
  1558.  
  1559.      The use of a compound key can be avoided by packing the text number
  1560. and line number into a single long integer as shown in the following DDL
  1561. and C code fragments.
  1562.  
  1563.     record text {                                   /* text record */
  1564.         unique key t_ulong  tx_textid;              /* text id */
  1565.         t_string            tx_line[LINLEN_MAX];    /* line of text */
  1566.     };
  1567.  
  1568.     text.tx_textid = textno << 8 | lineno;
  1569.  
  1570. Placing the text number in the higher order bytes has the effect of
  1571. making it the major sort.
  1572.  
  1573.  
  1574.  
  1575.  
  1576.                                  Appendix A:  Installation Instructions
  1577.  
  1578.      cbase is distributed in DOS format on either a 3.5" DSDD
  1579. (double-sided, double-density) or a 5.25" DSDD diskette.  The files are
  1580. compressed into a single archive, and the appropriate archive utility
  1581. will be required to unarchive the files.  The currently available archive
  1582. formats are ZIP and ZOO.  The commands to unarchive for each of these
  1583. formats are:
  1584.  
  1585.      pkunzip filename.zip
  1586.      zoo -extract filename.zoo
  1587.  
  1588.      Any operating system besides DOS will require either a facility to
  1589. read DOS diskettes or access to an DOS machine from which files can be
  1590. transferred (e.g., by a serial link or network) to the target machine. 
  1591. If the transfer process does not automatically convert the text files to
  1592. the format of the target system, an additional conversion utility will
  1593. be necessary; if using FTP (Internet File Transfer Protocol), the ascii
  1594. command will turn on text file translation.
  1595.  
  1596.      Where not explicitly stated otherwise, the following instructions
  1597. assume:  a DOS system, installation from drive A: to drive C:, the ZIP
  1598. archive format, an include directory \usr\include, a library directory
  1599. \usr\lib, and Borland Turbo C.  RL is used to indicate where a release
  1600. and level number appear in a filename(i.e., cbaseRL.zip would actually
  1601. be something like cbase102.zip).
  1602.  
  1603.      The first steps in the installation are to create a cbase directory
  1604. in the filesystem, copy the distribution diskette to this directory, and
  1605. unarchive the distribution.
  1606.  
  1607.      C:\> mkdir cbase
  1608.      C:\CBASE> cd cbase
  1609.      C:\CBASE> xcopy a:\ .
  1610.      C:\CBASE> pkunzip cbaseRL.zip
  1611.  
  1612.      Before proceeding any further, any readme files should be scanned
  1613. for last-minute notes; readme files have the extension .rme.  If the
  1614. installation is an upgrade, the file rlsnotes.txt should be read
  1615. carefully before compiling any existing applications.
  1616.  
  1617.      Among the files extracted from the archive will be several subset
  1618. archives.  These include:
  1619.  
  1620.     blkioRL.zip    blkio library
  1621.     btreeRL.zip    btree library
  1622.     lseqRL.zip    lseq library
  1623.     cbase.zip    cbase library
  1624.     manxRL.zip    manx utility
  1625.     rolodeck.zip    example program
  1626.     *bats.zip    DOS batch files for additional compilers
  1627.  
  1628. Each of these should be unarchived in its own subdirectory.
  1629.  
  1630.      C:\CBASE> mkdir manx
  1631.      C:\CBASE\MANX> cd manx
  1632.      C:\CBASE\MANX> pkunzip ..\manxRL.zip
  1633.  
  1634. manx is used to extract an on-line copy of the reference manual.
  1635.  
  1636.      At this point all the libraries, utilities, examples, etc. are
  1637. unarchived in separate directories, and the main installation can begin. 
  1638. Details steps are given in the following sections for each currently
  1639. supported operating system.
  1640.  
  1641.      If an upgrade from previous release is being performed, it is
  1642. essential that the libraries be installed in the correct order.  If the
  1643. new btree were installed while the old blkio header were still in use,
  1644. the results can be unpredictable.
  1645.  
  1646.      The DOS installation batch files, install.bat, each take two
  1647. arguments.  The first specifies the memory model, legal values for which
  1648. are s, m, c, l, and h; the library file is named MLIB.lib, where LIB
  1649. would be the library name and M would correspond to the memory model of
  1650. the library.  The second, if present, causes the reference manual to be
  1651. extracted from the source code into the file LIB.man, where LIB would
  1652. again be the library name.  The main batch file included with each
  1653. library is written for Borland Turbo C.  Because there is so little
  1654. uniformity among C compilers for DOS, modifications will be required for
  1655. other compilers.  Instructions for making these straightforward
  1656. modifications are given at the beginning of each install.bat.  Some batch
  1657. files modified for other compilers can be found in archives of the form
  1658. *bats.zip included in the distribution (e.g., bcbats.zip for Borland C++
  1659. and mscbats for Microsoft C), while additional ports may be found on the
  1660. Citadel BBS.  If a make utility is available, the UNIX makefiles may
  1661. instead be adapted.
  1662.  
  1663.      Common to all systems is the ANSI compatibility header <ansi.h>. 
  1664. This header contains a number of macros that are used to specify what
  1665. ANSI features are supported by the compiler being used.  For instance,
  1666. the AC_PROTO definition would be removed if function prototyping is not
  1667. supported.  As shipped, <ansi.h> is set up for a fully ANSI compiler. 
  1668. See the <ansi.h> manual entry or the man header of <ansi.h> itself for
  1669. more detailed instructions.
  1670.  
  1671.      If no multiuser applications are to be developed, file locking can
  1672. be disabled by defining the macro SINGLE_USER in blkio_.h.  This is
  1673. primarily intended to allow DOS applications to run without share being
  1674. loaded, and for older UNIX systems without file locking.  It will still
  1675. be necessary for an application to call the lock functions to set the
  1676. flags monitored internally by the libraries.  If SINGLE_USER is not
  1677. defined under DOS, then share must be loaded for a cbase application to
  1678. run.  DOS only provides exclusive locks, so two processes cannot have the
  1679. same cbase read-locked concurrently.
  1680.  
  1681.  
  1682. A1.  manx
  1683.  
  1684.                                   DOS
  1685.  
  1686.      1. Edit then install ANSI compatibility header.
  1687.              > copy ansi.h c:\usr\include
  1688.      2. Compile manx.
  1689.              > tcc -O -A -ms manx.c
  1690.      3. Install manx in a directory in the path.
  1691.              > copy manx.exe c:\usr\bin
  1692.  
  1693.  
  1694.                                  UNIX
  1695.      1. Edit then install ANSI compatibility header.
  1696.              $ su
  1697.              # cp ansi.h /usr/include
  1698.              # ^d
  1699.      2. Compile manx.
  1700.              $ make manx
  1701.      3. Install manx in a directory in the path.
  1702.              $ su
  1703.              # make install
  1704.              # ^d
  1705.      4. Extract the on-line reference manual.
  1706.              $ make man
  1707.  
  1708.  
  1709. A2.  The blkio Library
  1710.  
  1711.                                   DOS
  1712.  
  1713.      1. Set the OPSYS macro in blkio_.h to OS_DOS.
  1714.      2. Set the CCOM macro in blkio_.h to the C compiler being
  1715.         used.
  1716.      3. Reinstate the SINGLE_USER macro in blkio_.h if no multiuser
  1717.         applications will be developed.
  1718.      4. If necessary, modify install.bat for the C compiler being
  1719.         used.
  1720.      5. Extract the reference manual and build and install the blkio
  1721.         library.
  1722.              > install l x
  1723.         Run again for each additional memory model desired, without the
  1724.         x argument.
  1725.  
  1726.  
  1727.                                  UNIX
  1728.  
  1729.      1. Install the boolean header file.
  1730.              $ su
  1731.              # cp bool.h /usr/include
  1732.              # ^d
  1733.      2. Set the OPSYS macro in blkio_.h to OS_UNIX.
  1734.      3. Set the CCOM macro in blkio_.h to the C compiler being
  1735.         used.
  1736.      4. Reinstate the SINGLE_USER macro in blkio_.h if no multiuser
  1737.         applications will be developed.
  1738.      5. Extract the on-line reference manual.
  1739.              $ make man
  1740.      6. Build the blkio library.
  1741.              $ make blkio
  1742.      7. Install the blkio library.  This will copy the blkio header
  1743.         file blkio.h to /usr/include and the blkio library archive
  1744.         to /usr/lib.
  1745.              $ su
  1746.              # make install
  1747.              # ^d
  1748.  
  1749.  
  1750. A3.  The lseq Library
  1751.  
  1752.                                   DOS
  1753.  
  1754.      1. Install the blkio library.
  1755.      2. If necessary, modify install.bat for the C compiler
  1756.         being used.
  1757.      3. Install the lseq library.
  1758.              > install l x
  1759.         Run again for each additional memory model desired, without the
  1760.         x argument.
  1761.  
  1762.  
  1763.                                  UNIX
  1764.  
  1765.      1. Install the blkio library.
  1766.      2. Extract the on-line reference manual.
  1767.              $ make man
  1768.      3. Build the lseq library.
  1769.              $ make lseq
  1770.      4. Install the lseq library.  This will copy lseq.h to
  1771.         /usr/include and the lseq library archive to /usr/lib.
  1772.              $ su
  1773.              # make install
  1774.              # ^d
  1775.  
  1776.  
  1777. A4.  The btree Library
  1778.  
  1779.                                   DOS
  1780.  
  1781.      1. Install the blkio library.
  1782.      2. If necessary, modify install.bat for the C compiler
  1783.         being used.
  1784.      3. Install the btree library.
  1785.              > install l x
  1786.         Run again for each additional memory model desired, without the
  1787.         x argument.
  1788.  
  1789.  
  1790.                                  UNIX
  1791.  
  1792.      1. Install the blkio library.
  1793.      2. Extract the on-line reference manual.
  1794.              $ make man
  1795.      3. Build the btree library.
  1796.              $ make btree
  1797.      4. Install the btree library.  This will copy btree.h to
  1798.         /usr/include and the btree library archive to
  1799.         /usr/lib.
  1800.              $ su
  1801.              # make install
  1802.              # ^d
  1803.  
  1804.  
  1805. A5.  The cbase library
  1806.  
  1807.                                   DOS
  1808.  
  1809.      1. Install the btree and lseq libraries.
  1810.      2. If necessary, modify install.bat for the C compiler
  1811.         being used.
  1812.      3. Install the cbase library.
  1813.              > install l x
  1814.         Run again for each additional memory model desired, without the
  1815.         x argument.
  1816.  
  1817.  
  1818.                                  UNIX
  1819.  
  1820.      1. Install the btree and lseq libraries.
  1821.      2. Extract the on-line reference manual.
  1822.              $ make man
  1823.      3. Build the cbase library.
  1824.              $ make cbase
  1825.      4. Install the cbase library.  This will copy cbase.h to
  1826.         /usr/include and the cbase library archive to/usr/lib.
  1827.              $ su
  1828.              # make install
  1829.              # ^d
  1830.  
  1831. A6.  Combining Libraries
  1832.  
  1833.      To shorten the command line required to link a cbase application,
  1834. it may be desirable to combine the cbase libraries.
  1835.  
  1836.  
  1837.                                   DOS
  1838.  
  1839.      1. Build the combined library (large model).
  1840.              > tlib lcbasec.lib +lcbase.lib +llseq.lib +lbtree.lib
  1841.                                                      +lblkio.lib
  1842.      2. Install the combined library.
  1843.              > copy lcbasec.lib \usr\lib
  1844.      3. Report for other memory models.
  1845.  
  1846.  
  1847.                                  UNIX
  1848.  
  1849.      1. Build the combined library.
  1850.              $ ar rv cbasec cbase lseq btree blkio
  1851.      2. Install the combined library.
  1852.              $ su
  1853.              # mv cbasec /usr/lib/libcbasec.a
  1854.              # ^d
  1855.  
  1856.  
  1857. A7.  cbddlp
  1858.  
  1859.      In addition to a C compiler, cbddlp required the parser-generator
  1860. yacc and the lexical analyzer-generator lex.  Since these are not yet
  1861. widely used on DOS systems, an executable cbddlp for DOS is included with
  1862. cbase.
  1863.  
  1864.                                   DOS
  1865.  
  1866.      1. Install cbddlp in a directory in the path.
  1867.              > copy cbddlp.exe c:\usr\bin
  1868.  
  1869.  
  1870.                                  UNIX
  1871.  
  1872.      1. Install the cbase libraries.
  1873.      2. Set the PATHDLM macro in cbddlp.h to '/'.
  1874.      3. Extract the on-line reference manual.
  1875.              $ make man
  1876.      4. Compile cbddlp.
  1877.              $ make cbddlp
  1878.      5. Install cbddlp in a directory in the path.
  1879.              $ su
  1880.              # make install
  1881.              # ^d
  1882.  
  1883.  
  1884. A8.  rolodeck
  1885.  
  1886.                                   DOS
  1887.      1. Install cbase.
  1888.      2. If necessary, modify install.bat for the C compiler
  1889.         being used.
  1890.      3. Compile rolodeck, and extract the reference manual.
  1891.              > install l x
  1892.  
  1893.  
  1894.                                  UNIX
  1895.      1. Install cbase.
  1896.      2. Compile rolodeck.
  1897.              $ make rolodeck
  1898.      3. Extract the reference manual.
  1899.              $ make man
  1900.  
  1901.  
  1902. A9.  Troubleshooting
  1903.  
  1904.                                 Compile
  1905.  
  1906. Warnings
  1907.     During the course of the installation the compiler may issue a
  1908.     number of warnings.  In particular, "code not reached" is to be
  1909.     expected throughout, and "unused function parameter" may occur a
  1910.     number of times in cbcmp.c, cbexp.c, and cbimp.c.  These warnings
  1911.     should cause no concern, and no attempt should be made to quell
  1912.     them by editing the source.  The "code not reached warnings" are
  1913.     due to breaks in switch statements following a return or continue,
  1914.     and these have been placed there intentionally.  The lint program
  1915.     checker under UNIX provides the -b option to suppress warnings
  1916.     about superfluous breaks, but most DOS C compilers regrettably have
  1917.     no such option.  The "unused function parameter" warnings result
  1918.     from functions that are accessed internally through arrays and so
  1919.     must all have the same parameter list, even though some do not have
  1920.     the need to reference all the parameters.
  1921.  
  1922. Errors
  1923.     First check that OPSYS and CCOM have been defined correctly in
  1924.     blkio_.h, then that <ansi.h> has been set up correctly for the
  1925.     compiler being used.  If upgrading, be certain that the libraries
  1926.     are being installed in the correct order, otherwise a high-level
  1927.     library might be compiled with the header from an older low-level
  1928.     library.  It the source of the error cannot be determined and
  1929.     corrected, upload the following to the Citadel BBS:  the
  1930.     install.bat file being used, a dump of the compiler error message,
  1931.     and details of the system configuration (operating system,
  1932.     compiler, versions of each).  A message giving the name of the
  1933.     upload file should be addressed to Tech Support.
  1934.  
  1935.  
  1936.                                  Link
  1937.  
  1938. Command Line Too Long
  1939.     Use the combined library cbasec to shorten the command line.  See
  1940.     Appendix A6 for instructions on building a combined library.
  1941.  
  1942. Symbol Defined More Than Once
  1943.     If the named symbol is not defined in the application itself, then
  1944.     the conflict is between two libraries being used.  If the source
  1945.     is not available for either of those libraries, then little can be
  1946.     done.  Since cbase comes with complete source, a duplicated
  1947.     symbol here can be changed to eliminate the conflict.  Be certain
  1948.     to recompile the library containing the altered symbol as well as
  1949.     any higher-layer libraries, in ascending order.
  1950.  
  1951.  
  1952.                                Execution
  1953.  
  1954. Under DOS, cbcreate returns EINVAL, but all arguments are correct.
  1955.     Make sure share is loaded.  share should be in either config.sys
  1956.     or autoexec.bat to ensure that it is always loaded.
  1957.  
  1958. Under DOS, the maximum open file limit is exceeded.
  1959.     A series of steps is required to increase the number of open
  1960.     database files allowed.  First, the size of the system file table
  1961.     must be increased to at least the required limit using the FILES
  1962.     command in config.sys.  Second, the process file descriptor table
  1963.     must be enlarged by using the fdcset function included with the
  1964.     rolodeck example program.  Lastly, the file tables in each of the
  1965.     cbase libraries must be increased to the desired limit by changing
  1966.     the macros *OPEN_MAX in each of the library header files, then
  1967.     recompiling; it is essential that the libraries be recompiled in
  1968.     the correct order.
  1969.  
  1970.  
  1971.  
  1972.  
  1973.  
  1974.                                    Appendix B:  Defining New Data Types
  1975.  
  1976.  
  1977.      cbase is designed to allow custom data types to be defined by the
  1978. user.  Custom data types are currently implemented in exactly the same
  1979. way as the predefined types and become indistinguishable from those
  1980. predefined.  A data type definition consists of a macro used as the type
  1981. name (e.g., t_string), and three functions:  a comparison function, an
  1982. export function, and an import function.  The comparison function is the
  1983. most important; it determines the sort order for data of that type.  The
  1984. export function is used to export data of the associated type to a text
  1985. file, and the import function to import data.  Below are given
  1986. step-by-step instructions for defining a new cbase data type.
  1987.  
  1988.  
  1989. B1.  The Type Name
  1990.  
  1991.      For each cbase data type there is a corresponding type name by which
  1992. the user refers to that data type.  Type names are macros that must be
  1993. defined as integers starting at zero and increasing in steps of one. 
  1994. The type name for a new data type would be added at the end of this list,
  1995. and be defined as an integer one greater than the last data type in the
  1996. list.  To avoid possible conflict with future predefined types, user
  1997. defined type names should not start with t_; the prefix ut_ is
  1998. recommended.  The type names are macros defined in <cbase.h>.
  1999.  
  2000.     #define t_char      (0)     /* signed character */
  2001.     ...
  2002.     #define t_binary    (26)    /* binary data */
  2003.     #define ut_new      (27)    /* new data type */
  2004.  
  2005.  
  2006. B2.  The Comparison Function
  2007.  
  2008.      A data type is characterized primarily by its sort order.  Each data
  2009. type is given a comparison function defining this sort order.  Comparison
  2010. functions are of the form
  2011.  
  2012.     int cmp(const void *p1, const void *p2, size_t n);
  2013.  
  2014. p1 and p2 are pointers to two data items to be compared, and n is the
  2015. size of the data items.  The value returned must be less than, equal to,
  2016. or greater than zero if the data item pointed to by p1 is less than,
  2017. equal to, or greater than, respectively, that pointed to by p2.  The C
  2018. standard library function memcmp would be a valid cbase comparison
  2019. function.
  2020.  
  2021.      All cbase comparison functions are located in the file cbcmp.c. 
  2022. For a new data type, a comparison function would be added in this file. 
  2023.  
  2024.     static int newcmp(const void *p1, const void *p2, size_t n)
  2025.     {
  2026.         ...
  2027.     }
  2028.  
  2029.      Comparison functions are made static because they are accessed by
  2030. cbase only through an array of function pointers, cbcmpv, also defined
  2031. in cbcmp.c.  This array contains the comparison function for each cbase
  2032. data type.  The integer value of the type name is used by cbase as an
  2033. index into this array, and so it is absolutely necessary that the
  2034. comparison functions must be in the same order as the type names.  A
  2035. pointer to the comparison function for a new data type would be added at
  2036. the end of this array.
  2037.  
  2038.     /* cbase comparison function table */
  2039.     cbcmp_t cbcmpv[] = {
  2040.         charcmp,    /* t_char */
  2041.         ...
  2042.         bincmp,     /* t_binary */
  2043.         newcmp,     /* ut_new */
  2044.     };
  2045.  
  2046.  
  2047. B3.  The Export and Import Functions
  2048.  
  2049.      Each data type has an associated export function.  This export
  2050. function takes a data item of the associated type and writes it to a file
  2051. in a text format.  Export functions are of the form
  2052.  
  2053.     int exp(FILE *fp, const void *p, size_t n);
  2054.  
  2055. p is a pointer to the data item of size n to be exported.  The export
  2056. function converts the data item to text, then writes it to the current
  2057. position in file fp.  Upon successful completion, a value of zero is
  2058. returned.  Otherwise, a value of -1 is returned.  See the cbexport
  2059. reference manual entry for special requirements on exported data.
  2060.  
  2061.      All cbase export functions are located in the file cbexp.c.  For a
  2062. new data type, an export function would be added in this file.
  2063.  
  2064.     static int newexp(FILE *fp, const void *p, size_t n)
  2065.     {
  2066.         ...
  2067.     }
  2068.  
  2069.      Just as with comparison functions, export functions are accessed by
  2070. cbase through an array.  This array, cbexpv, is defined in cbexp.c.  A
  2071. pointer to the export function for the new data type would be added at
  2072. the end of this array.
  2073.  
  2074.      The import function reads a data item from a text file.  Import
  2075. functions are of the form
  2076.  
  2077.     int imp(FILE *fp, void *p, size_t n);
  2078.  
  2079. The parameters and return value are the same as for the export function. 
  2080. Import functions are located in cbimp.c.  Pointers to the import
  2081. functions are stored in the array cbimpv.
  2082.  
  2083.  
  2084. B4.  The Type Count
  2085.  
  2086.      The macro CBTYPECNT is defined in cbase_.h as the number of data
  2087. types defined.  It must be incremented by one for each new data type
  2088. added.
  2089.  
  2090.  
  2091.      After completing these steps, the cbase library must be rebuilt (see
  2092. Appendix A) to make the new data type accessible.  The underlying
  2093. libraries do not need to be rebuilt.
  2094.  
  2095.  
  2096.  
  2097.  
  2098.                          Appendix C:  Porting to a New Operating System
  2099.  
  2100.  
  2101.      The blkio library provides a means for portable access to structured
  2102. files just as the stdio library does for text files.  blkio is thus the
  2103. only library requiring modification to port to a new operating system. 
  2104. Layering within the library further isolates the modifications to just
  2105. three files.  The steps necessary to perform this port are outlined
  2106. below.
  2107.  
  2108.  
  2109. C1.  The OPSYS and CCOM Macros
  2110.  
  2111.      In the blkio library's private header file blkio_.h, a macro is
  2112. defined for each supported operating system.  When installing the blkio
  2113. library, the host operating system is selected by defining the OPSYS
  2114. macro as one of these OS macros.  When porting to a new operating system,
  2115. an OS macro definition for that system must be added in blkio_.h.  These
  2116. macros are given names of the form OS_* and assigned unique integers.
  2117.  
  2118.     #define OS_UNIX (1)     /* UNIX */
  2119.     #define OS_DOS  (2)     /* DOS */
  2120.     #define OS_NEW  (3)     /* new OS */
  2121.     #define OPSYS   OS_NEW
  2122.  
  2123.      In many instances it is necessary to take into account differences
  2124. between the C compilers available for a system beyond the ANSI
  2125. compatibility handled by <ansi.h>.  As with the operating system, a macro
  2126. is defined for each supported C compiler, and the compiler selected with
  2127. the CCOM macro in blkio_.h.  When porting to a new C compiler, a CC macro
  2128. definition for that compiler must be added in blkio_.h.  These macros are
  2129. given names of the form CC_* and assigned unique integers.
  2130.  
  2131.     #define CC_BC   (1)     /* Borland C */
  2132.     #define CC_MSC  (2)     /* Microsoft C */
  2133.     #define CC_NEW  (3)     /* new C compiler */
  2134.     #define CCOM    CC_NEW
  2135.  
  2136.  
  2137. C2.  The File Descriptor Type
  2138.  
  2139.      In most operating systems, an open file is accessed not by name,
  2140. but through some sort of tag, usually called a file descriptor.  File
  2141. descriptors are normally of type int, but blkio uses a union for the file
  2142. descriptor in order to enable it to handle any type.  This union is
  2143. defined in blkio_.h.
  2144.  
  2145.     typedef union {     /* file descriptor type */
  2146.         char    c;      /* character */
  2147.         short   s;      /* short int */
  2148.         int     i;      /* int */
  2149.     } fd_t;
  2150.  
  2151.      fd_t is used exclusively for the fd member of the BLKFILE structure.
  2152.  
  2153.     typedef struct {    /* block file ctl struct */
  2154.         fd_t    fd;     /* file descriptor */
  2155.         ...
  2156.     } BLKFILE;
  2157.  
  2158. When modifying the code in subsequent sections, the appropriate member
  2159. of the union fd_t would be used to access a file descriptor.  If the file
  2160. descriptor type for the new system is short, for instance, the file
  2161. descriptor for BLKFILE *bp would be accessed as bp->fd.s.  It will be
  2162. necessary to add a member to the fd_t union if one of the required type
  2163. does not already exist.
  2164.  
  2165.  
  2166. C3.  System Calls for File Access
  2167.  
  2168.      The bulk of the operating system specific code is related to the
  2169. system calls used to access the file system.  These system calls perform
  2170. basic operations such as opening, reading, and writing a file, and are
  2171. conceptually the same on most systems.  In fact, they can usually be
  2172. directly translated to a corresponding call on the new system.
  2173.  
  2174.      All system calls accessing the file system are isolated in the file
  2175. buops.c (blkio unbuffered operations).  The OPSYS and CCOM macros are
  2176. used to separate sections of code for different operating systems and
  2177. compilers, respectively.
  2178.  
  2179.     #if OPSYS == OS_DOS
  2180.         /* code for DOS */
  2181.     #if CCOM == CC_BC
  2182.         /* code for Borland C */
  2183.         .
  2184.         .
  2185.     #elif CCOM == CC_MSC
  2186.         /* code for Microsoft C */
  2187.         .
  2188.         .
  2189.     #endif
  2190.     #elif OPSYS == OS_UNIX
  2191.         /* code for UNIX */
  2192.         .
  2193.         .
  2194.     #endif
  2195.  
  2196. When porting to a new operating system or compiler, each of these
  2197. conditional compilations must be located and an additional #elif for the
  2198. new OS or CC macro added.
  2199.  
  2200.  
  2201. C4.  System Calls for File Locking
  2202.  
  2203.      System calls are also used to perform file locking.  All system
  2204. calls for file locking are located in the file lockb.c.  This file must
  2205. be modified in the same manner as buops.c.  If file locking will not be
  2206. used on the new system, lockb.c need not be altered.
  2207.  
  2208.  
  2209. C5.  Debugging
  2210.  
  2211.      Each library's private header file (blkio_.h, btree_.h, etc.)
  2212. contains a macro DEBUG whose definition has been commented out. 
  2213. Reinstating this macro will enable the debugging code within the library,
  2214. which includes such things as checking arguments passed to internal
  2215. functions for validity.  With debugging enabled, a diagnostic trace will
  2216. be generated for any abnormal error that occurs.  How this trace is
  2217. reported is controlled by the "error print" macro in the same header as
  2218. the DEBUG definition.  The error print macros are BEPRINT for blkio,
  2219. BTEPRINT for btree, etc.  As distributed, these macros use fprintf to
  2220. write the filename, line number, and value of errno to stderr.  For a
  2221. windowing system it will be necessary to modify these to log the trace
  2222. to a file.
  2223.  
  2224.  
  2225.  
  2226.  
  2227.                                                              References
  2228.  
  2229.  
  2230. AHO88    Aho, A., Kernighan B., and Weinberger P. The AWK
  2231.     Programming Language. Reading, MA: Addison-Wesley, 1988.
  2232.  
  2233. CALI82Calingaert, P. Operating System Elements. Englewood
  2234.     Cliffs, NJ: Prentice Hall, 1982.
  2235.  
  2236. COME79Comer, D. The Ubiquitous B-tree. ACM Computing
  2237.     Surveys, June 1979.
  2238.  
  2239. FROS89Frost, L. A Buffered I/O Library for Structured Files.
  2240.     The C Users Journal, October 1989.
  2241.  
  2242. HORO76Horowitz, E. and S. Sahni. Fundamentals of Data
  2243.     Structures. Rockville, MD: Computer Science Press, 1976.
  2244.  
  2245. KERN88Kernighan, B. and D. Ritchie. The C Programming
  2246.     Language. Englewood Cliffs, NJ: Prentice Hall, 1988.
  2247.  
  2248. KNUT68Knuth D. The Art of Computer Programming Volume 3 /
  2249.     Sorting and Searching. Reading, MA: Addison-Wesley, 1968.
  2250.  
  2251. ULLM82Ullman, J. Principles of Database Systems. Rockville,
  2252.      MD: Computer Science Press, 1982.